﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;

namespace Calculations.Calculations
{
    public class GeometryCalculations
    {
        /// <summary>
        /// Вращение точки против часовой стрелки на указанный угол с указанным центром вращения.
        /// </summary>
        /// <param name="pointToRotate">Точка, которую нужно повернуть.</param>
        /// <param name="rotateCenter">Центр вращения.</param>
        /// <param name="angle">Угол, на который будет повёрнута точка (поворот осушествляется против часовой стрелки).</param>
        /// <returns>Точка, получившаяся при повороте.</returns>
        public static PointF RotatePoint(PointF pointToRotate, PointF rotateCenter, double angle)
        {
            PointF point = new PointF(pointToRotate.X - rotateCenter.X, pointToRotate.Y - rotateCenter.Y);
            PointF result = new PointF();
            result.X = (float)(point.X * Math.Cos(angle) - point.Y * Math.Sin(angle));
            result.Y = (float)(point.X * Math.Sin(angle) + point.Y * Math.Cos(angle));
            result.X += rotateCenter.X;
            result.Y += rotateCenter.Y;
            return result;
        }

        public static Vector RotateVector(Vector vectorToRotate, double angle)
        {
            PointF vectorPoint = new PointF((float)vectorToRotate.X, (float)vectorToRotate.Y);
            PointF point = GeometryCalculations.RotatePoint(vectorPoint, new PointF(0, 0), angle);
            Vector result = new Vector(point.X, point.Y);
            return result;
        }

        public static double GetDistance(PointF fstPoint, PointF secPoint)
        {
            return Math.Sqrt(Math.Pow(fstPoint.X - secPoint.X, 2) + Math.Pow(fstPoint.Y - secPoint.Y, 2));
        }

        public static Vector GetNormalLine(PointF startPoint, PointF endPoint, double offset)
        {
            Vector tangent = new Vector(endPoint.X - startPoint.X, endPoint.Y - startPoint.Y);
            tangent = tangent.Normalize();
            tangent = GeometryCalculations.RotateVector(tangent, Math.PI / 2);
            return tangent;
        }

        public static PointF GetOffsetPoint(PointF startPoint, Vector directionVector)
        {
            PointF result = new PointF((float)(startPoint.X + directionVector.X), (float)(startPoint.Y + directionVector.Y));
            return result;
        }


        /// <summary>
        /// Нахождение угла, образованного тремя точками.
        /// </summary>
        /// <param name="cornerPoint">Точка, в которой образуется угол.</param>
        /// <param name="fstEndPoint">Первая точка</param>
        /// <param name="secEndPoint"></param>
        /// <returns></returns>
        //public static double GetAngle(PointF cornerPoint, PointF fstEndPoint, PointF secEndPoint)
        //{
        //    // Длины сторон треугольника, который образуют точки
        //    double a = GeometryCalculations.GetDistance(fstEndPoint, secEndPoint);
        //    double b = GeometryCalculations.GetDistance(cornerPoint, fstEndPoint);
        //    double c = GeometryCalculations.GetDistance(cornerPoint, secEndPoint);
        //    // Теорема косинусов
        //    double cosA = (a * a - b * b - c * c) / (-2 * b * c);
        //    return Math.Acos(cosA);
        //}

        public static double GetAngle(PointF cornerPoint, PointF fstEndPoint, PointF secEndPoint)
        {
            Vector _fstVector = new Vector(fstEndPoint.X - cornerPoint.X, fstEndPoint.Y - cornerPoint.Y).Normalize();
            Vector _secVector = new Vector(secEndPoint.X - cornerPoint.X, secEndPoint.Y - cornerPoint.Y).Normalize();

            double angle = Math.Acos((_fstVector.X * _secVector.X + _fstVector.Y * _secVector.Y)); // / (_fstVector.Length * _secVector.Length));
            Vector rotatedVector = GeometryCalculations.RotateVector(_secVector, -angle);
            if (rotatedVector.Equals(_fstVector))
                return angle;
            else
                return 2 * Math.PI - angle;
        }

        /// <summary>
        /// Определяет, совпадают ли точки с точностью tolerance. 
        /// </summary>
        /// <param name="first">Первая точка.</param>
        /// <param name="second">Вторая точка.</param>
        /// <param name="tolerance">Максимальное расстояние между точками, при котором точки будут считаться совпадающими.</param>
        /// <returns>True, если точки совпадают, иначе false.</returns>
        public static bool IsEqual(PointF first, PointF second, double tolerance)
        {
            if (GeometryCalculations.GetDistance(first, second) < tolerance)
                return true;
            else
                return false;
        }

        public static bool IsEqual(double first, double second, double tolerance)
        {
            if (Math.Abs(first - second) <= tolerance)
                return true;
            else
                return false;
        }

        public static T Min<T>(T fst, T sec) where T : IComparable, ICloneable
        {
            if (fst.CompareTo(sec) == -1)
                return fst;
            else
                return sec;
        }
    }
}
